package com.xtm.exercise.base.A08Collection.A01list.stream;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 类描述：文档地址：https://blog.csdn.net/mu_wind/article/details/109516995
 */
@Slf4j
public class StreamDemo {

    @Test
    public void test01(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 1,"male", "New York"));
        personList.add(new Person("Jack", 7000, 2,"male", "Washington"));
        personList.add(new Person("Lily", 7800, 3,"female", "Washington"));
        personList.add(new Person("Anni", 8200, 4,"female", "New York"));
        personList.add(new Person("Owen", 9500, 5,"male", "New York"));
        personList.add(new Person("Alisa", 7900, 1,"female", "New York"));

        // 非空判断
        if(CollUtil.isNotEmpty(personList)){
            System.out.println("CollUtil-非空");
        }
        if(CollectionUtils.isEmpty(personList)){
            System.out.println("CollectionUtils-为空");
        }

        // 遍历/匹配（foreach/find/match）
        //遍历输出符合条件的元素
        personList.stream().filter(x -> x.getAge() == 1).forEach(n -> System.out.println(n));
        personList.stream().filter(x -> x.getAge() == 1).forEach(n -> System.out.println(n.getSalary()));

        //获取第一条记录 get(0)
        Person person1 = personList.stream().filter(x -> x.getAge() == 1).findFirst().orElse(null);
        System.out.println(person1);

        // 获取集合大小 size  count 获取数量
        long size = personList.stream().count();

        //是否包含符合特定条件的元素
        boolean b = personList.stream().anyMatch(x -> x.getAge() == 1);
        System.out.println(b);

        // 过滤筛选（filter）
        //筛选员工中工资高于8000的人，并形成新的集合。 形成新集合依赖collect（收集）
        List<Person> list01 = personList.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toList());
        System.out.println(list01);

        List<String> list = Arrays.asList("apple", "banana", "orange");
        List<String> list3 = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
        System.out.println(list3);

        // 聚合（max/min/count)
        // 取最大值   获取员工薪资最高的人
        Optional<Person> max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
        Person person = personList.stream().max(Comparator.comparingInt(x -> x.getSalary())).orElse(null);
        System.out.println(max.get().getSalary());

        // 取最小值   获取员工薪资最低的人
        Optional<Person> min = personList.stream().min(Comparator.comparingInt(Person::getSalary));
        System.out.println(min.get().getSalary());

        //计算Integer集合中大于6的元素的个数。
        List<Integer> list33 = Arrays.asList(7, 6, 4, 8, 2, 11, 9);

        long count = list33.stream().filter(x -> x > 6).count();
        System.out.println("list中大于6的元素个数：" + count);

        // 映射(map/flatMap)   映射成新的流，就是处理数据
        //英文字符串数组的元素全部改为大写
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> collect = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

        // 使用map方法，把字符串类型的整数，转换（映射）为Integer类型的整数
        List<String> list1 = Arrays.asList("1", "2", "3", "4", "5");
        Stream<Integer> integerStream = list1.stream().map(n -> {
            return Integer.parseInt(n);
        });
        System.out.println(integerStream);

        List<String> list4 = Arrays.asList("apple", "banana", "orange");
        List<Integer> list5 = list4.stream().map(s -> s.length()).collect(Collectors.toList());
        System.out.println(list5);

        //整数数组每个元素+3
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> collect1 = intList.stream().map(x -> x + 3).collect(Collectors.toList());

        //将员工的薪资全部增加1000。
        //改变原来员工集合的方式
        List<Object> collect2 = personList.stream().map(p -> {
            p.setSalary(p.getSalary() + 1000);
            return p;
        }).collect(Collectors.toList());
        System.out.println(collect2);

    }

    @Test
    public void test02(){
        // 归约(reduce)
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
        Integer sum1 = list.stream().reduce(0, (a, b) -> a + b);

        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        Integer sum3 = list.stream().reduce(0, Integer::sum);

        // 求乘积
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);

        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2
        Integer max2 = list.stream().reduce(1, Integer::max);

        System.out.println("list求和：" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求积：" + product.get());
        System.out.println("list求最大值：" + max.get() + "," + max2);

        List<BigDecimal> bigDecimalList = Arrays.asList(new BigDecimal("1.7"), new BigDecimal("2.4"));
        // BigDecimal 求和
        BigDecimal sumBig = bigDecimalList.stream().filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println(sumBig);


    }

    @Test
    public void test03(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        // 求工资之和方式1：
        Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        // 求工资之和方式2：
        Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
                (sum1, sum2) -> sum1 + sum2);
        // 求工资之和方式3：
        Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

        // 求最高工资方式1：
        Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
                Integer::max);
        // 求最高工资方式2：
        Integer maxSalary2 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
                (max1, max2) -> max1 > max2 ? max1 : max2);
        // 求最高工资方式3：
        Integer maxSalary3 = personList.stream().map(Person::getSalary).reduce(Integer::max).get();

        System.out.println("工资之和：" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3);
        System.out.println("最高工资：" + maxSalary + "," + maxSalary2 + "," + maxSalary3);
    }

    //收集(collect)
    //collect，收集，可以说是内容最繁多、功能最丰富的部分了。从字面上去理解，就是把一个流收集起来，最终可以是收集成一个值也可以收集成一个新的集合。
    //归集(toList/toSet/toMap)
    @Test
    public void test04(){
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person(null, 8900, 23, "male", "New York"));
//        personList.add(new Person("Tom", 9200, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));

        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
        System.out.println("toMap:" + map);

        // filter(Objects::nonNull)   用于过滤掉所有null元素
        Map<String, Person> personMap = personList.stream().filter(Objects::nonNull).collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("personMap" + JSONObject.toJSONString(personMap));
        Person tom = personMap.get("Tom");
        System.out.println("Tom=" + tom);

        Map<String, Integer> integerMap = personList.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
        System.out.println("integerMap" + JSONObject.toJSONString(integerMap));
        Integer integer = integerMap.get("Tom");
        System.out.println("integer=" + integer);

    }

    //统计(count/averaging)
    @Test
    public void test05(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

        // 求总数
        Long count = personList.stream().collect(Collectors.counting());
        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        // 求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

        System.out.println("员工总数：" + count);
        System.out.println("员工平均工资：" + average);
        System.out.println("员工工资总和：" + sum);
        System.out.println("员工工资所有统计：" + collect);
    }

    //分组(partitioningBy/groupingBy)
    @Test
    public void test06(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 11,"male", "New York"));
        personList.add(new Person("Jack", 7000, 11,"male", "Washington"));
        personList.add(new Person("Lily", 7800, 12,"female", "Washington"));
        personList.add(new Person("Anni", 8200, 14,"female", "New York"));
        personList.add(new Person("Owen", 9500, 15,"male", "New York"));
        personList.add(new Person("Alisa", 7900, 16,"female", "New York"));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        Map<String, List<Person>> collect = personList.stream().collect(Collectors.groupingBy(x -> x.getSex()));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况：" + part);
        System.out.println("员工按性别分组情况：" + group);
        System.out.println("员工按性别、地区：" + group2);
    }

    //接合(joining)
    //joining可以将stream中的元素用特定的连接符（没有的话，则直接连接）连接成一个字符串。
    @Test
    public void test07(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

        String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名：" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串：" + string);
    }

    //排序(sorted)
//    sorted，中间操作。有两种排序：
    //    sorted()：自然排序，流中元素需实现Comparable接口
    //    sorted(Comparator com)：Comparator排序器自定义排序
    @Test
    public void test08(){
        //将员工按工资由高到低（工资一样则按年龄由大到小）排序
        List<Person> personList = new ArrayList<Person>();

        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));

        // 按工资升序排序（自然排序）
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge))
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄自定义排序（降序）
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序：" + newList);
        System.out.println("按工资降序排序：" + newList2);
        System.out.println("先按工资再按年龄升序排序：" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序：" + newList4);


        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);//[1, 3, 2]

        // 排序
        List<Integer> list = list01.stream().sorted(Comparator.comparing(Integer::byteValue)).collect(Collectors.toList());
        System.out.println(list);

        List<Person> list1 = personList.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
        System.out.println(list1);
    }

    //提取/组合
    //流也可以进行合并、去重、限制、跳过等操作。
    @Test
    public void test09(){
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };

        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct：去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit：截取元素  限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip：跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并：" + newList);
        System.out.println("limit：" + collect);
        System.out.println("skip：" + collect2);

        List<Person> personList1 = new ArrayList<Person>();

        personList1.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList1.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList1.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList1.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList1.add(new Person("Sherry", 9000, 26, "female", "New York"));
        personList1.add(new Person("Sherry", 9000, 24, "female", "New York"));

        // 将相同属性的实体，去重
        List<Person> collect3 = personList1.stream().distinct().collect(Collectors.toList());
        System.out.println(collect3);

        long count = personList1.stream().distinct().count();
        // 校验是否有重复，不允许重复
        if(personList1.size() != count){
            log.info("不允许重复");
        }

    }

    /**
     * 截取元素 limit
     */
    @Test
    public void test011(){
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.limit(2);
        System.out.println(result.count());
    }

    /**
     * skip跳过元素
     */
    @Test
    public void test12(){
        //获取一个Stream流
        String[] arr = {"张三", "李四", "王五", "赵六", "田七"};
        Stream<String> stream = Stream.of(arr);
        //使用skip方法跳过前3个元素
        Stream<String> stream1 = stream.skip(3);
        //遍历stream2流
        stream1.forEach(name-> System.out.println(name));
    }

    @Test
    public void test14(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 1,"male", "New York"));
        personList.add(new Person("Jack", 7000, 2,"male", "Washington"));
        personList.add(new Person("Lily", 7800, 3,"female", "Washington"));
        personList.add(new Person("Anni", 8200, 4,"female", "New York"));
        personList.add(new Person("Owen", 9500, 5,"male", "New York"));
        personList.add(new Person("Alisa", 7900, 1,"female", "New York"));

        List<Person> list = personList.stream().filter(e -> (e.getName() == "Tom" || e.getSalary() == 9500 || e.getArea().contains("ork"))).collect(Collectors.toList());
        System.out.println(list);
    }

    /**
     * 获取Stream流的两种方式
     */
    @Test
    public void test002(){
        //第一种/////////////////////////////////////////////////
        //把集合转换为stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Map<String,String> map = new HashMap<>();
        //获取键，存储到一个Set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();

        //获取值，存储到一个Collection集合中
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();

        //获取键值对（键与值的映射关系 entrySet）
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();


        //第二种////////////////////////////////////////////////////////////////
        //把数组转换为Stream流  of
        Stream<Integer> stream6 = Stream.of(1,2,3,4,5);
        //可变参数可以传递数组
        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream7 = Stream.of(arr);
        String[] arr2 = {"a","ab","abc"};
        Stream<String> stream8 = Stream.of(arr2);
    }

    @Test
    public void test11(){
        // java 集合移除另一个集合的元素
        List<String> list1 = Arrays.asList("1", "3","2", "8", "11", "4" , null);
        List<String> list3 = list1.stream().filter(Objects::nonNull).collect(Collectors.toList());
//        List<String> list2 = Arrays.asList("1", "3");
        List<String> list2 = null;
        List<String> result = list3.stream().filter(e -> !list2.contains(e)).collect(Collectors.toList());
        System.out.println(result);

        for (int i = 0; i < result.size(); i++) {
            String s = result.get(i);
        }
    }

}
